# frozen_string_literal: true

require 'watirspec_helper'

module Watir
  describe Cookies do
    after { browser.cookies.clear }

    it 'gets an empty list of cookies' do
      browser.goto WatirSpec.url_for 'index.html'
      expect(browser.cookies.to_a).to eq []
    end

    it 'gets any cookies set' do
      browser.goto set_cookie_url

      verify_cookies_count 1

      cookie = browser.cookies.to_a.first
      expect(cookie[:name]).to eq 'monster'
      expect(cookie[:value]).to eq '1'
    end

    describe '#[]' do
      before do
        browser.goto set_cookie_url
        verify_cookies_count 1
      end

      it 'returns cookie by symbol name' do
        cookie = browser.cookies[:monster]
        expect(cookie[:name]).to eq('monster')
        expect(cookie[:value]).to eq('1')
      end

      it 'returns cookie by string name' do
        cookie = browser.cookies['monster']
        expect(cookie[:name]).to eq('monster')
        expect(cookie[:value]).to eq('1')
      end

      it 'returns nil if there is no cookie with such name' do
        expect(browser.cookies[:non_monster]).to be_nil
      end
    end

    it 'adds a cookie without options' do
      browser.goto WatirSpec.url_for 'index.html'
      verify_cookies_count 0

      browser.cookies.add 'foo', 'bar'
      verify_cookies_count 1
    end

    it 'adds a cookie with a string expires value' do
      browser.goto WatirSpec.url_for 'index.html'

      expire_time = ::Time.now + 10_000

      browser.cookies.add 'foo', 'bar', expires: expire_time.to_s

      cookie = browser.cookies[:foo]
      expect(cookie[:expires]).to be_a(::Time)
    end

    it 'adds a cookie with path',
       except: {browser: :ie, reason: 'path contains two slashes'} do
      browser.goto WatirSpec.url_for 'index.html'

      options = {path: '/set_cookie'}
      browser.cookies.add 'path', 'b', options

      expect(browser.cookies.to_a).to be_empty

      browser.goto set_cookie_url
      cookie = browser.cookies.to_a.find { |e| e[:name] == 'path' }

      expect(cookie[:name]).to eq 'path'
      expect(cookie[:value]).to eq 'b'
      expect(cookie[:path]).to eq '/set_cookie'
    end

    it 'adds a cookie with samesite value' do
      browser.goto WatirSpec.url_for 'index.html'

      options = {same_site: 'Strict'}
      browser.cookies.add 'samesite', 'strict', options

      cookie = browser.cookies.to_a.find { |e| e[:name] == 'samesite' }

      expect(cookie[:name]).to eq 'samesite'
      expect(cookie[:value]).to eq 'strict'
      expect(cookie[:same_site]).to eq 'Strict'
    end

    it 'adds a cookie with httponly value' do
      browser.goto WatirSpec.url_for 'index.html'

      options = {http_only: true}
      browser.cookies.add 'httponly', 'true', options

      cookie = browser.cookies.to_a.find { |e| e[:name] == 'httponly' }

      expect(cookie[:name]).to eq 'httponly'
      expect(cookie[:value]).to eq 'true'
      expect(cookie[:http_only]).to be true
      expect(browser.execute_script('return document.cookie')).to be_empty
    end

    it 'adds a cookie with expiration' do
      browser.goto WatirSpec.url_for 'index.html'

      expires = ::Time.now + 10_000
      options = {expires: expires}

      browser.cookies.add 'expiration', 'b', options
      cookie = browser.cookies.to_a.first

      expect(cookie[:name]).to eq 'expiration'
      expect(cookie[:value]).to eq 'b'

      expect((cookie[:expires]).to_i).to be_within(2).of(expires.to_i)
    end

    # Pending resolution of https://github.com/w3c/webdriver/issues/1571
    it 'adding cookie with security does not raise exception but can not be retrieved',
       except: [{browser: :firefox,
                 reason: 'https://github.com/mozilla/geckodriver/issues/1840'},
                {browser: %i[chrome edge], platform: :windows}] do
      browser.goto WatirSpec.url_for 'index.html'

      options = {secure: true}

      browser.cookies.add 'secure', 'b', options
      cookie = browser.cookies.to_a.find { |e| e[:name] == 'secure' }
      expect(cookie).to be_nil
    end

    it 'removes a cookie' do
      browser.goto set_cookie_url
      verify_cookies_count 1

      browser.cookies.delete 'monster'
      verify_cookies_count 0
    end

    it 'clears all cookies' do
      browser.goto set_cookie_url
      browser.cookies.add 'foo', 'bar'
      verify_cookies_count 2

      browser.cookies.clear
      verify_cookies_count 0
    end

    context 'when using file' do
      let(:file) { "#{Dir.tmpdir}/cookies" }

      before do
        browser.goto set_cookie_url
        browser.cookies.save file
      end

      describe '#save' do
        it 'saves cookies to file' do
          expect(File.read(file)).to eq(browser.cookies.to_a.to_yaml)
        end
      end

      describe '#load', except: {browser: :ie} do
        it 'loads cookies from file' do
          browser.cookies.clear
          browser.cookies.load file
          expected = browser.cookies.to_a
          actual = YAML.safe_load_file(file, permitted_classes: [::Symbol])

          expected.each { |cookie| cookie.delete(:expires) }
          actual.each { |cookie| cookie.delete(:expires) }

          expect(actual).to eq(expected)
        end
      end
    end

    def set_cookie_url
      # add timestamp to url to avoid caching in IE8
      WatirSpec.url_for('set_cookie/index.html') + "?t=#{::Time.now.to_i + ::Time.now.usec}"
    end

    def verify_cookies_count(expected_size)
      cookies = browser.cookies.to_a
      msg = "expected #{expected_size} cookies, got #{cookies.size}: #{cookies.inspect}"
      expect(cookies.size).to eq(expected_size), msg
    end
  end
end
